package com.ahwei.shop.auth.config;

import com.ahwei.shop.auth.security.filter.JsonAuthenticationFilter;
import com.ahwei.shop.auth.security.filter.JwtTokenFilter;
import com.ahwei.shop.auth.security.handler.MyAccessDeniedHandler;
import com.ahwei.shop.auth.security.handler.MyAuthenticationEntryPoint;
import com.ahwei.shop.auth.security.handler.MyAuthenticationFailureHandler;
import com.ahwei.shop.auth.security.handler.MyAuthenticationSuccessHandler;
import com.ahwei.shop.auth.security.servcie.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.Filter;

/**
 * @author ahWei
 * Email: ahwei163@qq.com
 * Date: 2022-05-19 16:57:17
 * Description: 这是一个Java文件！
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    /**
     * 定制AuthenticationManager
     * 例如，添加自定义用户服务
     *
     * @param auth ...Builder用于创建AuthenticationManager。 允许轻松构建内存身份验证，
     *             LDAP身份验证，基于JDBC的身份验证，添加UserDetailsService以及添加AuthenticationProvider。
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //passwordEncoder(new BCryptPasswordEncoder());
        // 重写该方法，添加自定义用户服务
        auth.userDetailsService(userDetailsService());
    }

    /**
     * 更改WebSecurity中的默认行为
     * 构建FilterChainProxy。一个FilterChainProxy包含多个过滤器链和一个Firewall
     * WebSecurity中的ignoring()用于配置不用安全处理的请求，如静态资源
     *
     * @param web `
     */
    @Override
    public void configure(WebSecurity web) {
        // 忽略静态资源
        web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
    }

    /**
     * 更改HttpSecurity中的默认访问规则
     * 配置登录方式和访问权限
     *
     * @param http ·
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //访问权限 // permitAll表示放过匹配的请求
                // 放过请求，不需要认证和授权
                .antMatchers("/third-party-login").permitAll()
                .antMatchers("/api").permitAll()
                /* // 验证时有前缀ROLE_，/whoim需要ROLE_admin角色才能访问
                .antMatchers("/whoim").hasRole("user")
                // 在access中以表达式方式调用
                .antMatchers("/api/teacher/**").access("hasRole('user')")
                */
                .anyRequest().authenticated()   // 所有请求都需要认证后才能访问
                .and()     // 结束前面配置，返回HttpSecurity对象并开启新的配置

                //登录方式
                .formLogin()   // 表单登录
                .and()

                //Spring Security5默认开启CSRF，这里禁用CSRF，否则无法提交表单
                //开启CSRF后，在请求中必须包含CSRF的token，否则会抛出异常
                .csrf().disable()
                //开启跨域
                .cors(Customizer.withDefaults())

                //默认异常处理
                .exceptionHandling()
                //未认证处理
                .authenticationEntryPoint(authenticationEntryPoint())
                // 没有权限处理
                .accessDeniedHandler(accessDeniedHandler())
                .and()  //禁止Session，否则有Session后可能会忽略Token，导致认证生效
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 替换UsernamePasswordAuthenticationFilter
        http.addFilterAt(jsonAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        // 解析JWT的Filter放在UsernamePasswordAuthenticationFilter前面
        http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 配置跨域问题
     * @return ·
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        configuration.setAllowCredentials(false);
        configuration.addAllowedOrigin("*");
        configuration.addAllowedHeader(CorsConfiguration.ALL);
        configuration.addAllowedMethod(CorsConfiguration.ALL);
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
    /**
     * 身份验证入口点
     * 默认情况下登陆失败会跳转页面，这里自定义，同时判断是否ajax请求，是ajax请求则返回json,否则跳转失败页面
     * @return  `
     */
    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return new MyAuthenticationEntryPoint();
    }
    @Bean
    public GenericFilterBean jwtTokenFilter() {
        //访问时验证token
        return new JwtTokenFilter();
    }

    /**
     * 访问拒绝处理器、无权限
     * @return ·
     */
    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        return new MyAccessDeniedHandler();
    }

    /**
     * 身份认证成功处理
     * @return ·
     */
    @Bean
    public AuthenticationSuccessHandler myAuthenticationSuccessHandler() {
        return new MyAuthenticationSuccessHandler();
    }

    /**
     * 身份认证失败处理
     * @return ·
     */
    @Bean
    public AuthenticationFailureHandler myAuthenticationFailureHandler() {
        return new MyAuthenticationFailureHandler();
    }

    @Bean
    public Filter jsonAuthenticationFilter() throws Exception {
        JsonAuthenticationFilter filter = new JsonAuthenticationFilter();
        // 认证管理器需要自动配置
        filter.setAuthenticationManager(super.authenticationManagerBean());

        // 登录成功
        filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler());
        // 登录失败
        filter.setAuthenticationFailureHandler(myAuthenticationFailureHandler());
        return filter;
    }

    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        return new UserDetailsServiceImpl();
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}
